package com.ling.pl.security.utils;

import com.ling.pl.core.commons.exception.AccessDeniedException;
import com.ling.pl.core.commons.utils.PropertiesUtils;
import com.ling.pl.core.commons.utils.SpringUtil;
import com.ling.pl.core.context.ContextHolder;
import com.ling.pl.security.AuthorityType;
import com.ling.pl.security.decision.ComponentAccessDecisionManager;
import com.ling.pl.security.decision.UrlAccessDecisionManager;
import com.ling.pl.security.model.DefaultUser;
import com.ling.pl.security.service.DefaultUrlService;
import com.ling.pl.security.service.DefaultUserService;

import java.util.Collection;

/**
 * @author bo.wang
 */
public class SecurityUtils {
    /**
     * 用于检查当前给定的用户是否有权限访问给定的URL
     *
     * @param userName 要访问该资源的用户认证对象
     * @param url      目标URL
     * @throws AccessDeniedException
     */
    public static void checkUrl(String userName, String url) throws AccessDeniedException {
        DefaultUserService userService = SpringUtil.getBean(DefaultUserService.BEAN_ID);
        DefaultUser user = userService.loadUserByUsername(userName);
        if (user == null) {
            throw new AccessDeniedException("错误的用户名");
        }
        UrlMetadataSource metadata = ContextHolder.getBean(UrlMetadataSource.BEAN_ID);
        Collection<ConfigAttribute> SecurityConfigAttributes = metadata.getAttributes(url, user);
        //System.out.println(url+"size:"+SecurityConfigAttributes.size());
        if (SecurityConfigAttributes == null || SecurityConfigAttributes.size() == 0) {
            return;
        }

        UrlAccessDecisionManager decisionManager = ContextHolder.getBean(UrlAccessDecisionManager.BEAN_ID);
        decisionManager.decide(user, null, SecurityConfigAttributes);
    }

    /**
     * 判断指定人对指定的URL下的组件有没有访问权限
     *
     * @param authentication 要访问该URL下的组件的用户认证对象
     * @param type           要访问该目标组件的哪个权限属性，目前有两个，既读和写
     * @param componentId    组件的ID，注意，这里的组件ID必须包含其所属的URL，格式为url+"|"+componentId+"|"+AuthorityType+"|"+granted
     * @return 返回true表示有权限，false则表示无权限
     */
    public static boolean checkComponent(DefaultUser authentication, AuthorityType type, String componentId) {
        boolean componentPermissionWithoutURL = PropertiesUtils.getBoolean("ling2.enableComponentPermissionWithoutURL");
        boolean isRapidoComponent = false;
        String checkComponentId = componentId + "|" + type.toString();
        Collection<ConfigAttribute> SecurityConfigAttributes = getAttribute(checkComponentId, authentication);
        if (SecurityConfigAttributes == null || SecurityConfigAttributes.size() == 0) {
            if (componentPermissionWithoutURL) {
                String rapidoCompoentId = checkComponentId.substring(checkComponentId.indexOf("|"));
                SecurityConfigAttributes = getAttribute(rapidoCompoentId, authentication);
                if (SecurityConfigAttributes == null || SecurityConfigAttributes.size() == 0) {
                    return true;
                } else {
                    isRapidoComponent = true;
                }
            } else {
                return true;
            }
        }
        ComponentAccessDecisionManager decisionManager = ContextHolder.getBean(ComponentAccessDecisionManager.BEAN_ID);
        boolean granted = decisionManager.decide(authentication, SecurityConfigAttributes);
        if (type.equals(AuthorityType.read) && !granted) {
            Collection<ConfigAttribute> writeSecurityConfigAttributes;
            checkComponentId = componentId + "|" + AuthorityType.write.toString();
            if (isRapidoComponent) {
                String rapidoCompoentId = checkComponentId.substring(checkComponentId.indexOf("|"));
                writeSecurityConfigAttributes = getAttribute(rapidoCompoentId, authentication);
                if (SecurityConfigAttributes != null && SecurityConfigAttributes.size() > 0) {
                    if (!decisionManager.decide(authentication, writeSecurityConfigAttributes)) {
                        //在读写权限都没有的情况下，默认将直接给用户不能读的权限，也就是不允许用户看到组件
                        return false;
                    } else {
                        return true;
                    }
                }
            } else {
                writeSecurityConfigAttributes = getAttribute(checkComponentId, authentication);
                if (writeSecurityConfigAttributes != null && writeSecurityConfigAttributes.size() > 0) {
                    if (!decisionManager.decide(authentication, writeSecurityConfigAttributes)) {
                        //在读写权限都没有的情况下，默认将直接给用户不能读的权限，也就是不允许用户看到组件
                        return false;
                    } else {
                        return true;
                    }
                }
            }
        }
        return granted;
    }

    public static int getComponentAttributeSize(String componentId, AuthorityType type, DefaultUser authentication) {
        String checkComponentId = componentId + "|" + type.toString();
        Collection<ConfigAttribute> SecurityConfigAttributes = getAttribute(checkComponentId, authentication);
        if (SecurityConfigAttributes == null || SecurityConfigAttributes.size() == 0) {
            return 0;
        } else {
            return SecurityConfigAttributes.size();
        }
    }

    private static Collection<ConfigAttribute> getAttribute(String componentId, DefaultUser authentication) {
        ComponentMetadataSource metadata = ContextHolder.getBean(ComponentMetadataSource.BEAN_ID);
        Collection<ConfigAttribute> SecurityConfigAttributes = metadata.getAttributes(componentId, authentication);
        if (SecurityConfigAttributes == null || SecurityConfigAttributes.size() == 0) {
            componentId = componentId.substring(1, componentId.length());
            SecurityConfigAttributes = metadata.getAttributes(componentId, authentication);
        }
        return SecurityConfigAttributes;
    }

    public static void refreshUrlSecurityMetadata() {
        UrlMetadataSource metadata = ContextHolder.getBean(UrlMetadataSource.BEAN_ID);
        metadata.initUrlMetaData();
        DefaultUrlService urlService = ContextHolder.getBean(DefaultUrlService.BEAN_ID);
        urlService.cacheNavigatorUrls();
    }

    public static void refreshComponentSecurityMetadata() {
        ComponentMetadataSource metadata = ContextHolder.getBean(ComponentMetadataSource.BEAN_ID);
        metadata.initComponentMetadata();
    }
}
